using System;
using System.Collections;
using System.Collections.Generic;
using System.IdentityModel.Claims;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceProcess;
using Microsoft.MediaCenter;
using Microsoft.MediaCenter.UI;

namespace mcePicasa
{
    /// <summary>
    /// This is the object used by the mcePicasa application to interact with
    /// the DownloadManagerService. This proxy uses the code-generated
    /// interfaces from DownloadManagerServer.cs to interact with the
    /// Windows Service.
    /// Application holds a single instance of DownloadProxy and 
    /// the different ApplicationExperiences will call through this
    /// single instance to initiate a download.  This proxy will update
    /// the Progress property of the supplied SearchResult as updates
    /// come back from the download service.
    /// 
    /// For more information on the communication between mcePicasa and
    /// DownloadManagerService, see the comments in 
    /// DownloadManagerService's DownloadServer.cs file.
    /// </summary>
    internal class DownloadProxy : IDownloadManagerCallback
    {
        /// <summary>
        /// create a DownloadProxy and establish a connection with
        /// the DownloadManagerService
        /// </summary>
        public DownloadProxy()
        {
            // First, ensure that the DownloadManager service is running
            ServiceController downloadManagerController = new ServiceController("mcePicasa Download Manager");
            if (downloadManagerController == null)
            {
                // could not find service; inform user
                if (Application.Current.MediaCenterEnvironment != null)
                {
                    Application.Current.MessageBox(mcePicasa.Resources.DownloadFailedDialog_Text, mcePicasa.Resources.DownloadFailedDialog_Caption);
                }
                return;
            }
            if ((downloadManagerController.Status == ServiceControllerStatus.Stopped) ||
                (downloadManagerController.Status == ServiceControllerStatus.StopPending))
            {
                downloadManagerController.Start();
                downloadManagerController.WaitForStatus(ServiceControllerStatus.Running);
            }

            // Construct InstanceContext to handle messages on the 
            // IDownloadManagerCallback interface
            InstanceContext instanceContext = new InstanceContext(this);

            // svcutil.exe is a tool that you use to generate the client code associated
            // with a WCF (Windows Connected Framework) service.  This tool will generate
            // two files:
            //   DownloadManagerServer.cs: a C# file with the interfaces that are used in
            //                             the client-server
            //   App.config: an xml file that is used to configure the client to assure
            //               a proper communication pipeline between the client and the server.
            // Typically, a developer can include these files as-is in their client project.
            // But there's a snag for mcePicasa because mcePicasa is not a standalone executable (.exe), but is
            // rather a Media Center add-in (.dll).  App.config is a .exe concept, not a .dll
            // concept.  So it's necessary to convert the config file into programmatic
            // directives.
            // The generated App.config file looks like this:
            //    <?xml version="1.0" encoding="utf-8"?>
            //    <configuration>
            //      <system.serviceModel>
            //        <bindings>
            //          <netNamedPipeBinding>
            //            <binding name="ServiceEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
            //                receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false"
            //                transferMode="Buffered" transactionProtocol="OleTransactions"
            //                hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
            //                maxBufferSize="65536" maxConnections="10" maxReceivedMessageSize="65536">
            //              <security mode="Transport">
            //                <transport protectionLevel="EncryptAndSign" />
            //              </security>
            //            </binding>
            //          </netNamedPipeBinding>
            //        </bindings>
            //        <client>
            //          <endpoint address="net.pipe://localhost/DownloadManagerService"
            //              binding="netNamedPipeBinding" bindingConfiguration="ServiceEndpoint"
            //              contract="IDownloadManager" name="ServiceEndpoint">
            //            <identity>
            //              <servicePrincipalName value="DownloadServiceManager.exe" />
            //            </identity>
            //          </endpoint>
            //        </client>
            //      </system.serviceModel>
            //    </configuration>
            // And the programmatic equivalent of this configuration file is the following
            // code:

            NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport);
            binding.CloseTimeout = new TimeSpan(0, 1, 0);
            binding.OpenTimeout = new TimeSpan(0, 1, 0);
            binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
            binding.SendTimeout = new TimeSpan(0, 1, 0);
            binding.TransactionFlow = false;
            binding.TransferMode = TransferMode.Buffered;
            binding.TransactionProtocol = TransactionProtocol.OleTransactions;
            binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
            binding.MaxBufferPoolSize = 524288;
            binding.MaxBufferSize = 65536;
            binding.MaxConnections = 10;
            binding.MaxReceivedMessageSize = 65536;

            Claim claim = Claim.CreateSpnClaim("DownloadManagerService.exe");
            EndpointIdentity identity = EndpointIdentity.CreateIdentity(claim);
            EndpointAddress address = new EndpointAddress(new Uri("net.pipe://localhost/DownloadManagerService"), identity, (AddressHeaderCollection)null);

            // Create a client
            client = new DownloadManagerClient(instanceContext, binding, address);
        }

        /// <summary>
        /// This is the method called by the various application experiences
        /// in mcePicasa to initiate a download.
        /// </summary>
        public void InitiateDownload(ApplicationExperience experience, SearchResult item, Uri source, string destination)
        {
            if (client != null)
            {
                // call into the DownloadManagerService to initiate a download
                // of the indicated file
                client.Download(Application.Current.Username, source, destination, experience.Id, item.Id);

                // add this download to the list of active downloads
                experience.AddToActiveDownloads(item);
            }
        }


        /// <summary>
        /// This is the implemenation of the callback method called by
        /// the DownloadManagerService to give the client an update
        /// of download progress.
        /// </summary>
        public void UpdateDownloadedFraction(int experienceId, int itemId, float newFraction)
        {
            // Package up the data to send
            CallbackData data = new CallbackData(experienceId, itemId, newFraction);

            // Then send this to Media Center's UI thread;
            // This is necessary because DownloadProxy will act on this
            // call by updating data objects which the UI is actively 
            // showing and, thus, listening to changes.
            Microsoft.MediaCenter.UI.Application.DeferredInvoke(new DeferredHandler(NotifyDownloadedFractionUpdate), data);
        }

        /// <summary>
        /// This is the actual handling of the update notification from
        /// the DownloadManager.
        /// </summary>
        private void NotifyDownloadedFractionUpdate(object args)
        {
            // We are now on Media Center's UI thread; so now it is safe
            // to update data object state.

            CallbackData data = (CallbackData) args;

            // find the media item that has the given id
            ApplicationExperience experience = Application.Current.GetExperienceFromId(data.ExperienceId);
            foreach (SearchResult item in experience.ActiveDownloads)
            {
                if (item.Id == data.ItemId)
                {
                    // found media item; update the download %age
                    item.DownloadProgress = data.NewFraction;

                    if (data.NewFraction == 1.0)
                    {
                        // download complete -- remove this download from the active download lists
                        experience.RemoveFromActiveDownloads(item);
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// This is the data packet used to relay download information
        /// coming in from DownloadManagerService onto the UI thread
        /// </summary>
        class CallbackData
        {
            public CallbackData(int experienceId, int itemId, float newFraction)
            {
                ExperienceId = experienceId;
                ItemId = itemId;
                NewFraction = newFraction;
            }

            public int ExperienceId;
            public int ItemId;
            public float NewFraction;
        }

        DownloadManagerClient client;
    }
}
